package cn.exrick.xboot.modules.app.controller;

import cn.exrick.xboot.common.constant.CommonConstant;
import cn.exrick.xboot.common.constant.SecurityConstant;
import cn.exrick.xboot.common.enums.QrCodeEnmu;
import cn.exrick.xboot.common.qywxCode.Security.service.MyUserDetailsService;
import cn.exrick.xboot.common.utils.ResponseUtil;
import cn.exrick.xboot.common.utils.ResultUtil;
import cn.exrick.xboot.common.utils.SecurityUtil;
import cn.exrick.xboot.common.utils.SpringContextUtil;
import cn.exrick.xboot.common.vo.Result;
import cn.exrick.xboot.common.vo.TokenUser;
import cn.exrick.xboot.config.redis.RedisRepository;
import cn.exrick.xboot.modules.base.entity.User;
import cn.exrick.xboot.modules.base.service.UserService;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * @author    : LiYuanzhuo
 * description: 二维码
 * create time: 2019/9/10 17:00
 */
@Slf4j
@RestController
@RequestMapping(value = "/qrcode")
public class QRCodeController {
    @Value("${xboot.token.redis}")
    private Boolean tokenRedis;

    @Value("${xboot.tokenExpireTime}")
    private Integer tokenExpireTime;

    @Value("${xboot.saveLoginTime}")
    private Integer saveLoginTime;

    @Value("${xboot.token.storePerms}")
    private Boolean storePerms;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisRepository redisRepository;

    @Autowired
    private UserService userService;

    @Resource
    private MyUserDetailsService myUserDetailsService;


    /**
     * @author    : LiYuanzhuo
     * description: 生成二维码
     * create time: 2019/9/6 9:55
     */
    @RequestMapping(value = "/generateQRCode",method = RequestMethod.GET)
    public String generateQRCode(){
        String uid = UUID.randomUUID().toString();
        Date date = new Date();
        System.out.println("时间戳："+date.getTime());
        redisTemplate = (RedisTemplate) SpringContextUtil.getBean("redisTemplate");
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.opsForValue().set(CommonConstant.APP_QRCODE_FLAG + uid, uid, 60, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set(CommonConstant.APP_QRCODE_STATE + uid,QrCodeEnmu.logout.toString() , 60, TimeUnit.SECONDS);
        return uid;
    }

    /**
     * @author    : LiYuanzhuo
     * description: 轮训二维码接口
     * create time: 2019/9/6 10:13
     */
    @RequestMapping(value = "/qrcodeCheckLogin",method = RequestMethod.POST)
    public Result<Object> qrcodeCheckLogin(@RequestBody JSONObject jsonObject, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws InterruptedException {
        String uuid = jsonObject.containsKey("uuid") ? jsonObject.getString("uuid") : "";

        if ("".equals(uuid)) {
            return new ResultUtil<>().setErrorMsg("uuid为空");
        }
        //统一一个开始时间，每次请求超过10s时自动跳出循环结束
        long starttime = System.currentTimeMillis();
        while (true) {
            Thread.sleep(500);

            //检查redis是否中还存在二维码内容
            if (!redisRepository.exists(CommonConstant.APP_QRCODE_FLAG + uuid)) {
                return new ResultUtil<>().setErrorMsg(101,"二维码已失效，请重新获取!");
            } else {
                String status = redisRepository.get(CommonConstant.APP_QRCODE_STATE + uuid);
                //如果status 的值是 scan，则表示该code已经被手机扫描，返回页面提示在手机上确认登陆
                //如果status 的值是 login，则表示该code处于登录状态，则返回前端状态信息
                //如果status 的值是 cancel，则表示该code为取消登录
                //如果status 的值是 logout，则表示该code尚未被扫描

                if (QrCodeEnmu.logout.toString().equals(status)){
                    return new ResultUtil<>().setErrorMsg(101,"用户还未扫码登录！");
                }
                if (QrCodeEnmu.scan.toString().equals(status)) {
                    long endTime = System.currentTimeMillis();
                    long exeTime = endTime - starttime;
                    //请求大于10s，则跳出循环结束
                    if (exeTime >= 10000) {
                        break;
                    }else {
                        log.info("用户已经扫码！");
                        return new ResultUtil<>().setErrorMsg(101,"用户已经扫码！");
                    }
                }
                if (QrCodeEnmu.login.toString().equals(status)) {
                    log.info("登录成功之后，轮训停止");
                    //app已经扫码。并点击登录
                    String username = redisRepository.get(CommonConstant.APP_QRCODE_FLAG + uuid);

                    //获取用户信息
                    User user = userService.findByUsername(username);

                    if (user.getStatus().equals(CommonConstant.USER_STATUS_LOCK)){
                        return new ResultUtil<Object>().setErrorMsg("该用户已经被禁用！请联系管理员！");
                    }

                    //用户选择保存登录状态天几
                    String saveLogin = httpRequest.getParameter(SecurityConstant.SAVE_LOGIN);

                    Boolean saved = false;
                    if(StrUtil.isNotBlank(saveLogin) && Boolean.valueOf(saveLogin)){
                        saved = true;
                        if(!tokenRedis){
                            tokenExpireTime = saveLoginTime * 60 * 24;
                        }
                    }
                    SecurityUtil securityUtil = new SecurityUtil();
                    //用户权限放置
                    List<GrantedAuthority> authorities = securityUtil.getCurrUserPerms(username);

                    List<String> list = new ArrayList<>();
                    for(GrantedAuthority g : authorities){
                        list.add(g.getAuthority());
                    }
                    TokenUser users = new TokenUser(username, list, saved);

                    UserDetails userDetails = myUserDetailsService.loadUserByUsername(user);
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL);
                    SecurityContextHolder.getContext().setAuthentication(authentication);

                    String token = UUID.randomUUID().toString().replace("-", "");
                    // 单点登录 之前的token失效
                    String oldToken = stringRedisTemplate.opsForValue().get(SecurityConstant.USER_TOKEN + username);
                    if(StrUtil.isNotBlank(oldToken)){
                        redisTemplate.delete(SecurityConstant.TOKEN_PRE + oldToken);
                    }
                    stringRedisTemplate.opsForValue().set(SecurityConstant.USER_TOKEN + username, token, tokenExpireTime, TimeUnit.MINUTES);
                    stringRedisTemplate.opsForValue().set(SecurityConstant.TOKEN_PRE + token, new Gson().toJson(users), tokenExpireTime, TimeUnit.MINUTES);
                    Map map = new HashMap();
                    map.put("token",token);
                    map.put("userInfo",user);
                    ResponseUtil.out(httpResponse, ResponseUtil.resultMap(true,200,"登录成功", map));
                    break;

                }

            }

        }
        return new ResultUtil<>().setErrorMsg(1104, "请求超时");
    }

}
